package com.bytedance.frameworks.baselib.network.http.b.a;

import android.content.Context;

import com.bytedance.common.utility.Logger;
import com.bytedance.frameworks.baselib.network.connectionclass.ConnectionSamplingClient;
import com.bytedance.frameworks.baselib.network.http.AppConfig;
import com.bytedance.frameworks.baselib.network.http.BaseRequestContext;
import com.bytedance.frameworks.baselib.network.http.BaseRequestInfo;
import com.bytedance.frameworks.baselib.network.http.exception.HttpResponseException;
import com.bytedance.retrofit2.client.Client;
import com.bytedance.retrofit2.client.ClientCall;
import com.bytedance.retrofit2.client.Header;
import com.bytedance.retrofit2.client.Request;
import com.bytedance.retrofit2.client.SsResponse;
import com.bytedance.retrofit2.mime.TypedInput;
import com.bytedance.retrofit2.mime.TypedOutput;
import com.bytedance.retrofit2.mime.TypedString;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;

public class SsOkHttp3Client implements Client {
    public static final class OkHttp3ClientCall implements ClientCall {
        private OkHttpClient mOkHttpClient;
        private BaseRequestInfo requestInfo;
        private long mNewOkHttp3ClientCallTs;
        private String mTTLOGID;
        private Request mRequest;
        private Call mCall;

        public OkHttp3ClientCall(Request request) throws IOException {
            this.mOkHttpClient = null;
            this.requestInfo = BaseRequestInfo.createRequestInfo();
            this.mNewOkHttp3ClientCallTs = 0L;
            this.mTTLOGID = null;
            this.mOkHttpClient = SsOkHttp3Client.getOkHttp3Builder().build();
            this.mRequest = request;
            String url = this.mRequest.url;
            this.mNewOkHttp3ClientCallTs = System.currentTimeMillis();
            this.requestInfo.requestStartTs = this.mNewOkHttp3ClientCallTs;
            try {
                OkHttpClient.Builder builder = this.mOkHttpClient.newBuilder();
                builder.followRedirects = (this.mRequest.isStreaming)
                        || ("GET".equals(this.mRequest.method.toUpperCase()))
                        || !"POST".equals(this.mRequest.method.toUpperCase());
                builder.connectTimeout(((long) AppConfig.getConnectTimeout()), TimeUnit.MILLISECONDS);
                builder.readTimeout(((long) AppConfig.getReadWriteTimeout()), TimeUnit.MILLISECONDS);
                builder.writeTimeout(((long) AppConfig.getReadWriteTimeout()), TimeUnit.MILLISECONDS);
                if ((request.g instanceof BaseRequestContext)) {
                    this.requestInfo.requestContext = (BaseRequestContext) request.g;
                    BaseRequestContext requestContext = this.requestInfo.requestContext;
                    if (requestContext.timeout_connect <= 0L && requestContext.timeout_read <= 0L && requestContext.timeout_write <= 0L) {
                    } else {
                        if (requestContext.timeout_connect > 0L) {
                            builder.connectTimeout(requestContext.timeout_connect, TimeUnit.MILLISECONDS);
                        }

                        if (requestContext.timeout_write > 0L) {
                            builder.writeTimeout(requestContext.timeout_write, TimeUnit.MILLISECONDS);
                        }

                        if (requestContext.timeout_read <= 0L) {
                        } else {
                            builder.readTimeout(requestContext.timeout_read, TimeUnit.MILLISECONDS);
                        }
                    }
                }

                this.mOkHttpClient = builder.build();
                okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder().url(url);
                String method = this.mRequest.method;
                final TypedOutput typedOutput = this.mRequest.body;
                RequestBody requestBody;
                if (typedOutput == null) {
                    requestBody = null;
                } else {
                    final MediaType mediaType = MediaType.parse(typedOutput.mimeType());
                    requestBody = new RequestBody() {
                        public final MediaType contentType() {
                            return mediaType;
                        }

                        public final void writeTo(BufferedSink bufferedSink) throws IOException {
                            typedOutput.writeTo(bufferedSink.outputStream());
                        }

                        public final long contentLength() {
                            return typedOutput.length();
                        }
                    };
                }
                requestBuilder.method(method, requestBody);
                okhttp3.Request ok3Request = SsOkHttp3Client.buildOkHttp3Request(requestBuilder, this.mRequest.headers);
                this.mCall = this.mOkHttpClient.newCall(ok3Request);
                return;
            } catch (Exception e) {
                SsOkHttp3Client.onExecuteError(url, this.mNewOkHttp3ClientCallTs, this.requestInfo, this.mTTLOGID, e, this.mCall);
                if ((e instanceof IOException)) {
                    throw e;
                }
                throw new IOException(e.getMessage());
            }
        }

        private static List<Header> headers2headerList(Headers headers) {
            int size = headers.namesAndValues.length / 2;
            ArrayList<Header> headerArrayList = new ArrayList(size);
            for (int i = 0; i < size; ++i) {
                headerArrayList.add(new Header(headers.name(i), headers.value(i)));
            }

            return headerArrayList;
        }

        public final SsResponse execute() throws IOException {
            String requestUrl = this.mRequest.url;
            boolean rxBytesSampleEnable = false;
            try {
                if (this.mCall != null && this.mCall.isCanceled()) {
                    return null;
                } else {
                    if (SsOkHttp3Client.getApplicationContext() != null
                            && !android.support.design.a.isNetworkAvailable(SsOkHttp3Client.getApplicationContext())) {
                        return null;
                    }

                    if (!this.mRequest.isStreaming) {
                        AppConfig.RxBytesSampleController rxBytesSampleController = AppConfig.getRxBytesSampleController();
                        if (rxBytesSampleController != null && rxBytesSampleController.shouldSampleNet(requestUrl)) {
                            ConnectionSamplingClient.getInstance().startSampling();
                            rxBytesSampleEnable = true;
                        }
                    }
                }
                Response response = SsOkHttp3Client.execute(this.mOkHttpClient, this.mCall);
                this.requestInfo.responseBack = System.currentTimeMillis();
                if (this.mCall != null && this.mCall.streamAllocation() != null) {
                    this.requestInfo.recycleCount = this.mCall.streamAllocation().recycleCount;
                }
                this.mTTLOGID = SsOkHttp3Client.extractTTLOGID(response, this.requestInfo);
                int responseCode = response.code;
                String contentType = response.header("Content-Type");
                TypedInput typedInput;
                if (this.mRequest.isStreaming) {
                    String contentEncoding = response.header("Content-Encoding");
                    boolean gzip = contentEncoding != null && ("gzip".equalsIgnoreCase(contentEncoding));
                    if (response.body.contentLength() != 0) {
                        SsOkHttp3Response ssOkHttp3Response = new SsOkHttp3Response(response.body, gzip);
                        typedInput = ssOkHttp3Response;
                    } else {
                        typedInput = null;
                    }
                } else {
                    byte[] bytes = SsOkHttp3Client.convertResponse2Bytes(requestUrl, this.mRequest.f, response, this.mNewOkHttp3ClientCallTs, this.requestInfo, this.mTTLOGID);
                    typedInput = new TypedString(android.support.design.a.a(bytes, contentType));
                }

                SsResponse ssResponse = new SsResponse(requestUrl, responseCode, response.message,
                        OkHttp3ClientCall.headers2headerList(response.headers), typedInput);
                ssResponse.requestInfo = this.requestInfo;
                if (!this.mRequest.isStreaming && rxBytesSampleEnable) {
                    ConnectionSamplingClient connectionSamplingClient = ConnectionSamplingClient.getInstance();
                    connectionSamplingClient.stopSampling();
                }
                return ssResponse;
            } catch (Exception exception) {
                exception.printStackTrace();
                if ((exception instanceof HttpResponseException)) {
                    if (((HttpResponseException) exception).getStatusCode() == 304) {
                        throw (IOException) exception;
                    }
                }
                SsOkHttp3Client.onExecuteError(requestUrl, this.mNewOkHttp3ClientCallTs, this.requestInfo, this.mTTLOGID, exception, this.mCall);
                if ((exception instanceof IOException)) {
                    throw (IOException) exception;
                }
                throw new IOException(exception.getMessage());
            } finally {
                if (!this.mRequest.isStreaming && rxBytesSampleEnable) {
                    ConnectionSamplingClient.getInstance().stopSampling();
                }
            }
        }

        public final void cancel() {
            if (this.mCall != null) {
                this.mCall.cancel();
            }
        }

        public final Request request() {
            return this.mRequest;
        }
    }

    private static volatile SsOkHttp3Client sInstance = null;
    private static Context sApplicationContext;
    private static OkHttp3Builder sOkHttp3Builder;

    private SsOkHttp3Client(Context context) {
        sApplicationContext = context.getApplicationContext();
        sOkHttp3Builder = new OkHttp3Builder();
    }

    static OkHttp3Builder getOkHttp3Builder() {
        return SsOkHttp3Client.sOkHttp3Builder;
    }

    public static SsOkHttp3Client getInstance(Context context) {
        if (SsOkHttp3Client.sInstance == null) {
            synchronized (SsOkHttp3Client.class) {
                if (SsOkHttp3Client.sInstance == null) {
                    SsOkHttp3Client.sInstance = new SsOkHttp3Client(context);
                }
            }
        }
        return SsOkHttp3Client.sInstance;
    }

    private static String getHostAddress(Exception exception) {
        if (exception == null) {
            return "";
        }

        try {
            String[] segments = exception.getMessage().split("\\|");
            if (segments == null) {
                return "";
            }

            if (segments.length < 2) {
                return "";
            }

            if (Logger.debug()) {
                Logger.d("SsOkHttp3Client", "getHostAddress remoteIp = " + segments[0]);
            }

            return segments[0];
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return "";
    }

    static String extractTTLOGID(Response response, BaseRequestInfo baseRequestInfo) {
        return SsOkHttp3Client.doExtractTTLOGID(response, baseRequestInfo);
    }

    static okhttp3.Request buildOkHttp3Request(okhttp3.Request.Builder builder,
                                               List<Header> headerList) throws IOException {
        if (builder == null) {
            return null;
        } else {
            builder.addHeader("Accept-Encoding", "gzip");
            String userAgent = AppConfig.getUserAgent();
            if (!android.support.design.a.isTextEmpty(userAgent)) {
                builder.header("User-Agent", userAgent + " okhttp/3.7.0.2");
            }

            if (headerList != null) {
                Iterator<Header> iterator = headerList.iterator();
                while (iterator.hasNext()) {
                    Header header = iterator.next();
                    if (android.support.design.a.isTextEmpty(header.name)) {
                        continue;
                    }
                    if (android.support.design.a.isTextEmpty(header.value)) {
                        continue;
                    }
                    builder.header(header.name, header.value);
                }
            }
            return builder.build();
        }
    }

    static Response execute(OkHttpClient okHttpClient, Call call) throws IOException {
        return okHttpClient == null || call == null ? null : call.execute();
    }

    static void onExecuteError(String requestUrl, long newOkHttp3ClientCallTs, BaseRequestInfo baseRequestInfo, String ttLogId, Exception ex, Call call) {
        if (requestUrl != null) {
            if (baseRequestInfo != null && (android.support.design.a.isTextEmpty(baseRequestInfo.timingRemoteIP))) {
                SsOkHttp3Client.setRemoteIp(SsOkHttp3Client.getHostAddress(ex), baseRequestInfo);
            }
            long costTimeMS = System.currentTimeMillis() - newOkHttp3ClientCallTs;
            baseRequestInfo.requestEnd = System.currentTimeMillis();
            AppConfig.a(requestUrl, ex);
            AppConfig.a(costTimeMS, newOkHttp3ClientCallTs, requestUrl, ttLogId, baseRequestInfo, ex);
            if (call != null) {
                call.cancel();
            }
        }
    }

    private static void setRemoteIp(String remoteIp, BaseRequestInfo baseRequestInfo) {
        if (!android.support.design.a.isTextEmpty(remoteIp) && baseRequestInfo != null) {
            try {
                if (Logger.debug()) {
                    Logger.d("SsOkHttp3Client", "getRequestInfo remoteIp = " + remoteIp);
                }
                baseRequestInfo.timingRemoteIP = remoteIp;
                if (baseRequestInfo.requestContext == null) {
                    return;
                }
                baseRequestInfo.requestContext.remoteIp = remoteIp;
            } catch (Throwable throwable) {
            }
        }
    }

    static byte[] convertResponse2Bytes(String requestUrl, int arg2, Response response,
                                        long newOkHttp3ClientCallTs, BaseRequestInfo baseRequestInfo,
                                        String ttLogId) throws IOException {
        return SsOkHttp3Client.response2Bytes(requestUrl, arg2, response, newOkHttp3ClientCallTs, baseRequestInfo, ttLogId);
    }

    public final ClientCall execute(Request request) throws IOException {
        return new OkHttp3ClientCall(request);
    }

    private static String doExtractTTLOGID(Response response, BaseRequestInfo baseRequestInfo) {
        if (response == null) {
            return null;
        } else {
            SsOkHttp3Client.setRemoteIp(response.header("x-snssdk.remoteaddr"), baseRequestInfo);
            return response.header("X-TT-LOGID");
        }
    }


    private static byte[] response2Bytes(String requestUrl,
                                         int maxLength,
                                         Response response,
                                         long newOkHttp3ClientCallTs,
                                         BaseRequestInfo requestInfo,
                                         String ttLogId) throws IOException {
        if (response == null) {
            return new byte[0];
        } else {
            int statusCode = response.code;
            ResponseBody responseBody = response.body;
            if (statusCode == 200) {
                if (responseBody != null) {
                    byte[] bytes = responseBody.bytes();
                    requestInfo.fetchResponseTs = System.currentTimeMillis();
                    boolean gzip = "gzip".equals(response.header("Content-Encoding"));
                    if (gzip) {
                        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
                        int[] length = new int[]{0};
                        byte[] unzipBytes = android.support.design.a.unzipStream(gzip, maxLength, bais, length);
                        bais.close();
                        bytes = new byte[length[0]];
                        System.arraycopy(unzipBytes, 0, bytes, 0, length[0]);
                    }
                    if (android.support.design.a.hasSSMix(response.header("Content-Type"))) {
                        android.support.design.a.decodeBytes(bytes, bytes.length);
                    }
                    long costTimeMS = System.currentTimeMillis() - newOkHttp3ClientCallTs;
                    requestInfo.requestEnd = System.currentTimeMillis();
                    AppConfig.b(requestUrl);
                    AppConfig.a(costTimeMS, newOkHttp3ClientCallTs, requestUrl, ttLogId, requestInfo);
                    return bytes;
                } else {
                    return new byte[0];
                }
            } else {
                if (statusCode == 304) {
                    requestInfo.fetchResponseTs = System.currentTimeMillis();
                    long costTimeMS = System.currentTimeMillis() - newOkHttp3ClientCallTs;
                    requestInfo.requestEnd = System.currentTimeMillis();
                    AppConfig.b(requestUrl);
                    AppConfig.a(costTimeMS, newOkHttp3ClientCallTs, requestUrl, ttLogId, requestInfo);
                }
                if (responseBody != null) {
                    android.support.design.a.closeQuietly(responseBody, null);
                }
                throw new HttpResponseException(statusCode, response.message);
            }
        }
    }

    static Context getApplicationContext() {
        return SsOkHttp3Client.sApplicationContext;
    }
}

